package com.hrbxlh.support.attendance.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.hrbxlh.support.attendance.dao.AttenceClassCourseMapper;
import com.hrbxlh.support.attendance.dao.AttenceConfigMapper;
import com.hrbxlh.support.attendance.dao.AttendanceMapper;
import com.hrbxlh.support.attendance.dao.AttendanceMapperExt;
import com.hrbxlh.support.attendance.domain.*;
import com.hrbxlh.support.attendance.enums.AttendanceStateEnum;
import com.hrbxlh.support.attendance.po.AttendanceConfigPO;
import com.hrbxlh.support.attendance.po.AttendanceListQueryPO;
import com.hrbxlh.support.attendance.service.AttendanceConfigService;
import com.hrbxlh.support.attendance.vo.AttendanceConfigVO;
import com.hrbxlh.support.attendance.vo.AttendanceInfoVO;
import com.hrbxlh.support.attendance.vo.UserVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lx
 */
@Service
public class AttendanceConfigServiceImpl implements AttendanceConfigService {

    @Autowired
    private AttendanceMapper attendanceMapper;

    @Autowired
    private AttenceClassCourseMapper attenceClassCourseMapper;

    @Autowired
    private AttenceConfigMapper configMapper;

    @Autowired
    private AttendanceMapperExt attendanceMapperExt;

    @Override
    public List<AttendanceInfoVO> listAttendanceMobile(AttendanceListQueryPO queryPO) {
        List<UserVO> students = attendanceMapperExt.queryClassStudent(queryPO.getCourseId(),
                CollectionUtils.isEmpty(queryPO.getClassIdList()) ? null : queryPO.getClassIdList().get(0));
        List<AttendanceInfoVO> data = convertPageList(students, queryPO.getDate(), queryPO.getCourseId(),
                AttendanceStateEnum.getByMessage(queryPO.getAttendanceState()));
        return data;
    }

    @Override
    public AttendanceConfigVO queryConfig(Integer courseId, Integer classId) {

        // 获取考勤与课程班级配置基本数据
        List<AttenceClassCourse> data = queryAttenceClassCourse(courseId, classId);
        AttenceClassCourse attenceClassCourse = CollectionUtils.isEmpty(data) ? null : data.get(0);

        // 获取考勤详细配置
        List<AttenceConfig> attenceConfigs = null;
        if (attenceClassCourse != null) {
            attenceConfigs = queryAttenceConfig(attenceClassCourse.getId());
        }

        return convert(attenceClassCourse, attenceConfigs, courseId, classId);
    }

    @Override
    public void updateConfig(AttendanceConfigPO configPO) {

        // 转换数据
        AttenceClassCourse attenceClassCourse = new AttenceClassCourse();
        convertAttenceClassCourse(configPO, attenceClassCourse);

        // 修改考勤配置基础表
        List<AttenceClassCourse> attenceClassCourses = queryAttenceClassCourse(
                configPO.getCourseId(), configPO.getClassId());
        if (CollectionUtils.isEmpty(attenceClassCourses)) {
            // 插入
            attenceClassCourseMapper.insertSelective(attenceClassCourse);
        } else {
            // 修改
            AttenceClassCourse data = attenceClassCourses.get(0);
            attenceClassCourse.setId(data.getId());
            attenceClassCourse.setCreateTime(data.getCreateTime());
            attenceClassCourse.setUpdateTime(new Date());
            attenceClassCourseMapper.updateByPrimaryKey(attenceClassCourse);
        }

        // 修改考勤详细表
        deleteAttenceConfig(attenceClassCourse.getId());
        if (CollectionUtils.isEmpty(configPO.getInfo())) return;
        configPO.getInfo().forEach(attenceConfig -> {
            attenceConfig.setAttenceClassCourseId(attenceClassCourse.getId());
            configMapper.insertSelective(attenceConfig);
        });

        // 删除考勤
        deleteAttendance(configPO.getClassId(), configPO.getCourseId(),
                configPO.getInfo(), configPO.getStartTime(), configPO.getEndTime());

    }

    private boolean isWithin(Date startTime, Date endTime, Date time) {
        if (time.getTime() >= startTime.getTime() && time.getTime() <= endTime.getTime()) {
            return true;
        }
        return false;
    }

    private List<AttenceClassCourse> queryAttenceClassCourse(Integer courseId, Integer classId) {
        AttenceClassCourseExample example = new AttenceClassCourseExample();
        example.createCriteria().andCourseIdEqualTo(courseId).andClassIdEqualTo(classId);
        return attenceClassCourseMapper.selectByExample(example);
    }

    private List<AttenceConfig> queryAttenceConfig(Integer attenceClassCourseId) {
        AttenceConfigExample example = new AttenceConfigExample();
        example.createCriteria().andAttenceClassCourseIdEqualTo(attenceClassCourseId);
        return configMapper.selectByExample(example);
    }

    private void deleteAttenceConfig(Integer attenceClassCourseId) {
        AttenceConfigExample example = new AttenceConfigExample();
        example.createCriteria().andAttenceClassCourseIdEqualTo(attenceClassCourseId);
        configMapper.deleteByExample(example);
    }

    private AttendanceConfigVO convert(AttenceClassCourse data, List<AttenceConfig> attenceConfigs,
                                       Integer courseId, Integer classId) {
        return AttendanceConfigVO.builder()
                .switchOn(data == null ? false : data.getSwitchOn())
                .startTime(data == null ? null : data.getStartTime())
                .endTime(data == null ? null : data.getEndTime())
                .info(attenceConfigs)
                .classId(classId)
                .courseId(courseId)
                .build();
    }

    private void convertAttenceClassCourse(AttendanceConfigPO configPO, AttenceClassCourse data) {
        Date startTime = StringUtils.isBlank(configPO.getStartTime()) ?
                null : DateUtil.parse(configPO.getStartTime() + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN);
        Date endTime = StringUtils.isBlank(configPO.getEndTime()) ?
                null : DateUtil.parse(configPO.getEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN);
        data.setCourseId(configPO.getCourseId());
        data.setClassId(configPO.getClassId());
        data.setStartTime(startTime);
        data.setEndTime(endTime);
        data.setSwitchOn(configPO.isSwitchOn());
    }

    private Set<Integer> queryDeleteAttendanceIds(
            List<Attendance> attendances, List<AttenceConfig> info, String periodStartTimeStr, String periodEndTimeStr) {

        Date date = new Date();
        Date periodStartTime = DateUtil.beginOfDay(DateUtil.parseDate(periodStartTimeStr));
        Date periodEndTime = DateUtil.endOfDay(DateUtil.parseDate(periodEndTimeStr));
        String week = DateUtil.thisDayOfWeekEnum().toChinese();
        String dateStr = DateUtil.formatDate(date);

        Set<Integer> deleteAttendanceIds = Sets.newHashSet();
        attendances.forEach(attendance -> {
            info.forEach(attenceConfig -> {
                if (isWithin(periodStartTime, periodEndTime, date)) {
                    if (Objects.equals(attenceConfig.getWeek(), week)) {
                        String startTimeStr = dateStr + " " + attenceConfig.getStartTime();
                        String endTimeStr = dateStr + " " + attenceConfig.getEndTime();
                        if (isWithin(DateUtil.parse(startTimeStr, DatePattern.NORM_DATETIME_MINUTE_PATTERN),
                                DateUtil.parse(endTimeStr, DatePattern.NORM_DATETIME_MINUTE_PATTERN),
                                attendance.getCreateTime())) {
                            return;
                        } else {
                            deleteAttendanceIds.add(attendance.getId());
                        }
                    }
                }
            });
        });
        return deleteAttendanceIds;
    }

    private void deleteAttendance(Integer classId, Integer courseId, List<AttenceConfig> infos,
                                  String startTime, String endTime) {
        // 获取今天学生打卡的记录
        AttendanceExample example = new AttendanceExample();
        example.createCriteria().andClassIdEqualTo(classId).andCourseIdEqualTo(courseId)
                .andCreateTimeBetween(DateUtil.beginOfDay(new Date()), DateUtil.endOfDay(new Date()));
        List<Attendance> attendances = attendanceMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(attendances)) return;

        // 获取需要删除的考勤id集合，并删除
        Set<Integer> deleteAttendanceIds = queryDeleteAttendanceIds(attendances, infos, startTime, endTime);
        if (CollectionUtils.isNotEmpty(deleteAttendanceIds)) {
            example.clear();
            example.createCriteria().andIdIn(Lists.newArrayList(deleteAttendanceIds));
            attendanceMapper.deleteByExample(example);
        }
    }

    private List<AttendanceInfoVO> convertPageList(List<UserVO> students, String date,
                                                   Integer courseId, AttendanceStateEnum stateEnum) {

        if (CollectionUtils.isEmpty(students)) return Lists.newArrayList();

        // 获取学生的考勤
        List<Integer> classIds = students.stream().map(UserVO::getClassId).collect(Collectors.toList());

        Table<Integer, Integer, List<Attendance>> attendanceTable = HashBasedTable.create();
        Date startTime = DateUtil.parse(date + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN);
        Date endTime = DateUtil.parse(date + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN);

        AttendanceExample example = new AttendanceExample();
        example.or().andCreateTimeBetween(startTime, endTime)
                .andClassIdIn(classIds).andCourseIdEqualTo(courseId);
        List<Attendance> attendances = attendanceMapper.selectByExample(example);
        for (Attendance attendance : attendances) {
            List<Attendance> data = attendanceTable.get(attendance.getUserId(), attendance.getClassId());
            data = CollectionUtils.isEmpty(data) ? Lists.newArrayList() : data;
            data.add(attendance);
            attendanceTable.put(attendance.getUserId(), attendance.getClassId(), data);
        }

        List<AttendanceInfoVO> attendanceVOs = new ArrayList<>();
        for (UserVO user : students) {
            AttendanceInfoVO attendanceVO = AttendanceInfoVO.builder()
                    .userId(user.getUserId())
                    .username(user.getUsername())
                    .userNumber(user.getUserNumber())
                    .classId(user.getClassId())
                    .className(user.getClassName())
                    .build();

            List<Attendance> data = attendanceTable.get(user.getUserId(), user.getClassId());
            if (CollectionUtils.isNotEmpty(data)
                    && (stateEnum == null || stateEnum == AttendanceStateEnum.CLOCKED_IN)) {
                attendances.forEach(attendance -> {
                    attendanceVO.setState(AttendanceStateEnum.getMessageByCode(attendance.getStatus()));
                    attendanceVO.setClockTime(attendance.getCreateTime());
                    attendanceVOs.add(attendanceVO);
                });
            }
            if (CollectionUtils.isEmpty(data)
                    && (stateEnum == null || stateEnum == AttendanceStateEnum.TRUANT)) {
                attendanceVO.setState(AttendanceStateEnum.TRUANT.getMessage());
                attendanceVOs.add(attendanceVO);
            }
        }

        return attendanceVOs;
    }

}
